﻿using PoliticalClassifier.Domain;
using PoliticalClassifier.Support;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;

namespace PoliticalClassifier.DataSource
{
    /// <summary>
    /// DataSetReader provides an internal implementation of the IDataSetReader interface.
    /// </summary>
    internal class DataSetReader : IDataSetReader
    {
        #region Contruction
        /// <summary>
        /// Constructs a default data set reader.
        /// </summary>
        public DataSetReader()
        {
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Read data from a dataset and validate the results.
        /// </summary>
        /// <param name="dataSet">The dataset to read from.</param>
        /// <param name="loadResults">The list of errors and warnings that occured.</param>
        public void ReadAndValidate(DataSet dataSet, IDataSourceLoadResults loadResults)
        {
            if (dataSet == null || dataSet.Tables.Count <= 0)
            {
                loadResults.AddError(new DataSourceNoSheetError());
                return;
            }

            DataTable dataTable = dataSet.Tables["VotersDataSet"];
            if (!dataTable.Columns.Contains(Constants.COLUMN_NAME_ID))
            {
                loadResults.AddError(new DataSourceMissingIDColumnError());
                return;
            }

            if (dataTable.Columns.Count <= 1)
            {
                loadResults.AddError(new DataSourceNoWeightColumnsError());
                return;
            }

            if (dataTable.Rows.Count == 0)
            {
                loadResults.AddError(new DataSourceNoRowsError());
                return;
            }

            ColumnNames = new string[dataTable.Columns.Count];
            int i = 0;
            foreach (DataColumn column in dataTable.Columns)
                ColumnNames[i++] = column.ColumnName;

            i = 2;
            DataSourceOmittedRowsWarning omittedRowsWarning = new DataSourceOmittedRowsWarning();
            foreach (DataRow dataRow in dataTable.Rows)
            {
                if (!ReadDataRow(dataRow, dataTable.Columns))
                    omittedRowsWarning.OmittedRowNumbers.Add(i);
                ++i;
            }

            if (IgnoredRowsCount == dataTable.Rows.Count)
            {
                loadResults.AddError(new DataSourceNoValidRowsError());
                return;
            }
            else if (omittedRowsWarning.OmittedRowNumbers.Count > 0)
                loadResults.AddWarning(omittedRowsWarning);
        }
        #endregion

        #region Public Properties
        /// <summary>
        /// Gets the set of voter record data.
        /// </summary>
        public ICollection<IVoterRecord> VotersRecord
        {
            get
            {
                if (m_collectionVotersRecord == null)
                    m_collectionVotersRecord = new Collection<IVoterRecord>();
                return m_collectionVotersRecord;
            }
        }

        /// <summary>
        /// Gets the number of ignored rows.
        /// </summary>
        public int IgnoredRowsCount
        {
            get
            {
                return m_ignoredRowsCount;
            }
            private set
            {
                m_ignoredRowsCount = value;
            }
        }

        /// <summary>
        /// Gets the count of voter records.
        /// </summary>
        public int Count
        {
            get
            {
                return VotersRecord.Count;
            }
        }

        /// <summary>
        /// Gets the names of the voter attributes.
        /// </summary>
        public string[] ColumnNames
        {
            get { return m_columnNames; }
            private set { m_columnNames = value; }
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Reads the data from the given data row.
        /// </summary>
        /// <param name="dataRow">The data row to read from.</param>
        /// <param name="dataColumns">The columns to read.</param>
        /// <returns>Whether or not the row was read without error.</returns>
        private bool ReadDataRow(DataRow dataRow, DataColumnCollection dataColumns)
        {
            bool isInvalid = true;
            IVoterRecord voterRecord = null;

            if (dataRow[Constants.COLUMN_NAME_ID] != null && dataRow[Constants.COLUMN_NAME_ID] is double)
            {
                isInvalid = false;
                uint ID = Convert.ToUInt32(dataRow[Constants.COLUMN_NAME_ID]);
                voterRecord = new VoterRecord(ID);

                foreach(DataColumn column in dataColumns)
                {
                    if (column.ColumnName == Constants.COLUMN_NAME_ID)
                        continue;

                    if (dataRow[column.ColumnName] != null && dataRow[column.ColumnName] is double)
                    {
                        double value = (double)dataRow[column.ColumnName];
                        if (value >= 0.0 && value <= 1.0)
                            voterRecord.Attributes.Add(new VoterAttribute(column.ColumnName, (double)dataRow[column.ColumnName]));
                        else
                        {
                            isInvalid = true;
                            break;
                        }
                    }
                    else
                    {
                        isInvalid = true;
                        break;
                    }
                }
            }

            if (isInvalid)
            {
                IgnoredRowsCount++;
                return false;
            }
            else
            {
                VotersRecord.Add(voterRecord);
                return true;
            }
        }
        #endregion 

        #region Private Fields
        private ICollection<IVoterRecord> m_collectionVotersRecord;
        private int m_ignoredRowsCount;
        private string[] m_columnNames;
        #endregion
    }
}
